package com.ljh.mall.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.ljh.mall.product.service.CategoryBrandRelationService;
import com.ljh.mall.product.vo.Catalog2VO;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ljh.common.utils.PageUtils;
import com.ljh.common.utils.Query;

import com.ljh.mall.product.dao.CategoryDao;
import com.ljh.mall.product.entity.CategoryEntity;
import com.ljh.mall.product.service.CategoryService;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;


@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

    @Resource
    CategoryBrandRelationService categoryBrandRelationService;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    RedissonClient redissonClient;
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public List<CategoryEntity> queryPageWithTree(Map<String, Object> params) {
        //1 查询所有商品的分类信息
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);
        // 将商品分类信息拆分 成树形结构
        // 遍历所有 父类 parent_cid = 0
        List<CategoryEntity> list = categoryEntities.stream().filter(categoryEntity -> categoryEntity.getParentCid() == 0)
                // 根据父类 找到 对应的子类  递归
                .map(categoryEntity -> {
                    categoryEntity.setChildren(getCategoryChildrens(categoryEntity,categoryEntities));
                    return categoryEntity;
                }).sorted((entity1, entity2) -> {
                    return (entity1.getSort() == null ? 0: entity1.getSort()) - (entity2.getSort() == null ? 0: entity2.getSort());
                }).collect(Collectors.toList());

        return list;
    }

    @Override
    public void removeCategoryByIds(List<Long> ids) {
        baseMapper.deleteBatchIds(ids);
    }

    @Override
    public Long[] findCatelogPath(Long catelogId) {
        List<Long> paths = new ArrayList<>();
        List<Long> parentPath = findParentPath(catelogId, paths);
        // reverse的意思是反转，而不是降序。只是将list集合原来的顺序反转了一下
        Collections.reverse(parentPath);
        return parentPath.toArray(new Long[parentPath.size()]);
    }

    @Override
    public void updateDetail(CategoryEntity categoryEntity) {
        // 更新类别名称
        this.updateById(categoryEntity);
        if(!StringUtils.isEmpty(categoryEntity)){
            categoryBrandRelationService.updateCatelogName(categoryEntity.getCatId(),categoryEntity.getName());

        }
    }

    @Override
    public List<CategoryEntity> getLeve1Category() {
        long start = System.currentTimeMillis();
        List<CategoryEntity> list = baseMapper.queryLeve1Category();
        System.out.println("查询消耗的时间：" + (System.currentTimeMillis() - start));
        return list;
    }

    @Override
    public Map<String, List<Catalog2VO>> getCatelog2JSON() {
        // 解决缓存击穿方案：加锁，大量并发只让一个去查，其他等待，查到以后释放锁，其他获取到锁，查缓存就会有数据，不用查DB

        String key = "catalogJSON";
        // 从redis中获取分类的信息
        String catalogJSON = stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.isEmpty(catalogJSON)){
            System.out.println("缓存没有命中.....");
            // 缓存没有数据，从数据库中查找 getCatelog2JSONDbWithRedisson
//            Map<String, List<Catalog2VO>> catelog2JSONForDb = getCatelog2JSONForDb();
//            if(catelog2JSONForDb == null){
//                // 就说明数据库  不存在，缓存一个数据 1， 防止缓存穿透
//                stringRedisTemplate.opsForValue().set(key,"1",5, TimeUnit.SECONDS);
//            } else {
//                // 给缓存中存储
//                String json = JSON.toJSONString(catelog2JSONForDb);
//                // 缓存雪崩解决方案：原有的失效时间基础上增加一个随机值，使缓存过期时间的重复率低，就不会引发集体失效事件
//                stringRedisTemplate.opsForValue().set("catalogJSON",json,10,TimeUnit.MINUTES);
//            }
            Map<String, List<Catalog2VO>> catelog2JSONForDb = getCatelog2JSONDbWithRedisson();
            return catelog2JSONForDb;
        }
        System.out.println("缓存命中了....");
        Map<String, List<Catalog2VO>> stringListMap = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catalog2VO>>>() {});
        return stringListMap;

    }

    /**
     * redis setNx 分布式锁
     * @return
     */
    public Map<String, List<Catalog2VO>> getCatelog2JSONDbWithRedisLock() {
        String keys = "catalogJSON";
        String uuid = UUID.randomUUID().toString();
        // 加锁
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock",uuid,30,TimeUnit.SECONDS);
        if (lock) {
            //
            stringRedisTemplate.expire("lock", 20, TimeUnit.SECONDS);
            //
            Map<String, List<Catalog2VO>> data = getDataForDB(keys);
            //
            stringRedisTemplate.delete("lock");
            return data;


        } else {
            // 加锁失败
            // 休眠+重试
            // Thread.sleep(1000);
            return getCatelog2JSONDbWithRedisLock();
        }
    }

    /**
     * redisson 分布式锁
     * @return
     */
    public Map<String, List<Catalog2VO>> getCatelog2JSONDbWithRedisson(){
        String keys = "catalogJSON";
        // 获取分布式锁对象  加锁的时候，这个锁的名称一定要注意
        RLock lock = redissonClient.getLock("catelog2JSON-lock");
        Map<String, List<Catalog2VO>> data = null;
        try {
            lock.lock();
            //
            data = getDataForDB(keys);
        } finally {
            lock.unlock();
        }
        return data;
    }
    /**
     * 从数据库中查询数据
     * @param keys
     * @return
     */
    private Map<String, List<Catalog2VO>> getDataForDB(String keys){
        // 先去缓存中查询有没有数据，如果有就返回，否则查询数据库
        // 从redis中获取分类的信息
        String catalogJSON = stringRedisTemplate.opsForValue().get(keys);
        if(!StringUtils.isEmpty(catalogJSON)){
            //
            Map<String, List<Catalog2VO>> stringListMap = JSON.parseObject(catalogJSON,new TypeReference<Map<String, List<Catalog2VO>>>() {
            });
            return stringListMap;
        }
        System.out.println("-----------》查询数据库操作");

        //
        List<CategoryEntity> list = baseMapper.selectList(new QueryWrapper<CategoryEntity>());
        //获取所有的一级分类的数据
        List<CategoryEntity> leve1Category = this.queryByParenCid(list,0l);
        //
        Map<String, List<Catalog2VO>> map =leve1Category.stream().collect(Collectors.toMap(
                key -> key.getCatId().toString(),
                value -> {
                    // 根据一级分类的编号，查询出对应的二级分类的数据
                    List<CategoryEntity> l2Catalogs = this.queryByParenCid(list,value.getCatId());
                    List<Catalog2VO> Catalog2VOs =null;
                    if (l2Catalogs != null){
                        Catalog2VOs = l2Catalogs.stream().map(l2 -> {
                            // 需要把查询出来的二级分类的数据填充到对应的Catelog2VO中
                            Catalog2VO catalog2VOs = new Catalog2VO(l2.getParentCid().toString(), null, l2.getCatId().toString(), l2.getName());
                            // 根据二级分类的数据  找到  对应的三级分类的信息
                            List<CategoryEntity> l3Catelogs = this.queryByParenCid(list, l2.getCatId());
                            if (l3Catelogs != null) {
                                //
                                List<Catalog2VO.Catalog3VO> catalog3VOS = l3Catelogs.stream().map(l3 -> {
                                    Catalog2VO.Catalog3VO catalog3VO = new Catalog2VO.Catalog3VO(l3.getParentCid().toString(), l3.getCatId().toString(), l3.getName());
                                    return catalog3VO;
                                }).collect(Collectors.toList());
                                // 三级分类关联 二级分类

                                catalog2VOs.setCatalog3List(catalog3VOS);
                            }
                            return catalog2VOs;
                        }).collect(Collectors.toList());

                    }
                    return Catalog2VOs;
                }
        ));
        if (map == null){
            // 那就说明数据库中也不存在，防止缓存穿透
            String json = JSON.toJSONString(map);
            stringRedisTemplate.opsForValue().set(keys,json,5,TimeUnit.SECONDS);
        } else {
            // 备份到缓存
            // 防止雪崩
            String json = JSON.toJSONString(map);
            stringRedisTemplate.opsForValue().set("catalogJSON",json,10,TimeUnit.MINUTES);
        }
        return map;
    }

    @Override
    public Map<String, List<Catalog2VO>> getCatelog2JSONForDb() {
        String keys = "catalogJSON";
        synchronized (this) {
            // 先去缓存中查询有没有数据，如果有就返回，否则查询数据库
            // 从redis中获取分类的信息
            String catalogJSON = stringRedisTemplate.opsForValue().get(keys);
            if(!StringUtils.isEmpty(catalogJSON)){
                //
                Map<String, List<Catalog2VO>> stringListMap = JSON.parseObject(catalogJSON,new TypeReference<Map<String, List<Catalog2VO>>>() {
                });
                return stringListMap;
            }
            System.out.println("-----------》查询数据库操作");

        //
        List<CategoryEntity> list = baseMapper.selectList(new QueryWrapper<CategoryEntity>());
        //获取所有的一级分类的数据
        List<CategoryEntity> leve1Category = this.queryByParenCid(list,0l);
        //
        Map<String, List<Catalog2VO>> map =leve1Category.stream().collect(Collectors.toMap(
                key -> key.getCatId().toString(),
                value -> {
                    // 根据一级分类的编号，查询出对应的二级分类的数据
                    List<CategoryEntity> l2Catalogs = this.queryByParenCid(list,value.getCatId());
                    List<Catalog2VO> Catalog2VOs =null;
                    if (l2Catalogs != null){
                        Catalog2VOs = l2Catalogs.stream().map(l2 -> {
                            // 需要把查询出来的二级分类的数据填充到对应的Catelog2VO中
                            Catalog2VO catalog2VOs = new Catalog2VO(l2.getParentCid().toString(), null, l2.getCatId().toString(), l2.getName());
                            // 根据二级分类的数据  找到  对应的三级分类的信息
                            List<CategoryEntity> l3Catelogs = this.queryByParenCid(list, l2.getCatId());
                            if (l3Catelogs != null) {
                                //
                                List<Catalog2VO.Catalog3VO> catalog3VOS = l3Catelogs.stream().map(l3 -> {
                                    Catalog2VO.Catalog3VO catalog3VO = new Catalog2VO.Catalog3VO(l3.getParentCid().toString(), l3.getCatId().toString(), l3.getName());
                                    return catalog3VO;
                                }).collect(Collectors.toList());
                                // 三级分类关联 二级分类

                                catalog2VOs.setCatalog3List(catalog3VOS);
                            }
                            return catalog2VOs;
                        }).collect(Collectors.toList());

                    }
                    return Catalog2VOs;
                }
        ));
        if (map == null){
            // 那就说明数据库中也不存在，防止缓存穿透
            String json = JSON.toJSONString(map);
            stringRedisTemplate.opsForValue().set(keys,json,5,TimeUnit.SECONDS);
        } else {
            // 备份到缓存
            // 防止雪崩
            String json = JSON.toJSONString(map);
            stringRedisTemplate.opsForValue().set("catalogJSON",json,10,TimeUnit.MINUTES);
        }
        return map;
        }
    }

    private List<CategoryEntity> getCategoryChildrens(CategoryEntity categoryEntity, List<CategoryEntity> categoryEntities){
        // 二级 三级  四级、、、、
        List<CategoryEntity> collect = categoryEntities.stream().filter(entity ->{
            return entity.getParentCid().equals(categoryEntity.getCatId());
        }).map(entity -> {
            entity.setChildren(getCategoryChildrens(entity,categoryEntities));
            return entity;
        }).sorted((entity1, entity2) -> {
            return (entity1.getSort() == null ? 0 : entity1.getSort()) - (entity2.getSort() == null ? 0 : entity2.getSort());
        }).collect(Collectors.toList());
        return collect;
    }
    private List<Long> findParentPath(Long catelogId,List<Long> paths){
        paths.add(catelogId);
        CategoryEntity entity = this.getById(catelogId);
        if (entity.getParentCid() != 0){
            findParentPath(entity.getParentCid(),paths);
        }
        return paths;
    }

    /**
     * 根据父级编号获取对应的子菜单信息
     * @param list
     * @param parentCid
     * @return
     */
    private List<CategoryEntity> queryByParenCid(List<CategoryEntity> list,Long parentCid){
        List<CategoryEntity> collect = list.stream().filter(item ->{
            return item.getParentCid().equals(parentCid);
        }).collect(Collectors.toList());

        return collect;
    }

}